/*
 * ProcessMonitor.java
 *
 * Created on 11 January 2008, 14:51
 */

package nii.alloe.gui;
import javax.swing.*;
import java.io.*;
import java.util.*;
import nii.alloe.tools.process.AlloeProcess;
import nii.alloe.tools.process.AlloeProgressListener;
import nii.alloe.tools.process.CannotPauseException;

/**
 *
 * @author  john
 */
public class ProcessMonitor extends javax.swing.JPanel implements AlloeProgressListener {
    private JFileChooser fileChooser;
    private int state;
    private static final int STATE_NOTSTARTED = 0;
    private static final int STATE_RUNNING = 1;
    private static final int STATE_COMPLETED = 2;
    private static final int STATE_PAUSED = 3;
    
    /** Creates new form ProcessMonitor */
    public ProcessMonitor() {
        initComponents();
        state = STATE_NOTSTARTED;
        fileChooser = new JFileChooser();
        listeners = new LinkedList<AlloeProgressListener>();
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
        progressBar = new javax.swing.JProgressBar();
        startPauseButton = new javax.swing.JButton();
        resumeButton = new javax.swing.JButton();

        progressBar.setString(getProgressText());
        progressBar.setStringPainted(true);

        startPauseButton.setText("Start");
        startPauseButton.setEnabled(false);
        startPauseButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startPauseButtonActionPerformed(evt);
            }
        });

        resumeButton.setText("Resume");
        resumeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                resumeButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 283, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(startPauseButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(resumeButton)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(resumeButton)
                    .addComponent(startPauseButton)
                    .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 25, Short.MAX_VALUE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
    }// </editor-fold>//GEN-END:initComponents
    
    private void resumeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resumeButtonActionPerformed
        if(!onResume())
            return;
        if(fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
            return;
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileChooser.getSelectedFile()));
            Object o = ois.readObject();
            if(!(o instanceof AlloeProcess))
                JOptionPane.showMessageDialog(this, "Invalid format", "Could not resume", JOptionPane.ERROR_MESSAGE);
            setProcess((AlloeProcess)o);
            getProcess().addProgressListener(this);
            Iterator<AlloeProgressListener> listIter = listeners.iterator();
            while(listIter.hasNext())
                getProcess().addProgressListener(listIter.next());
            getProcess().resume();
            setState(STATE_RUNNING);
        } catch(IOException x) {
            JOptionPane.showMessageDialog(this, x.getMessage(), "Could not resume", JOptionPane.ERROR_MESSAGE);
        } catch(ClassNotFoundException x) {
            JOptionPane.showMessageDialog(this, x.getMessage(), "Could not resume", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_resumeButtonActionPerformed
    
    private void startPauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startPauseButtonActionPerformed
        if(state == STATE_NOTSTARTED || state == STATE_COMPLETED) {
            if(!onStart())
                return;
            process.addProgressListener(this);
            process.start();
            startPauseButton.setText("Pause");
            setState(STATE_RUNNING);
        } else if(state == STATE_RUNNING) {
            try {
                setState(STATE_PAUSED);
                if(fileChooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) {
                    setState(STATE_RUNNING);
                    return;
                }
                process.pause();
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileChooser.getSelectedFile()));
                oos.writeObject(process);
                oos.close();
                
            } catch(IOException x) {
                JOptionPane.showMessageDialog(this, x.getMessage(), "Could not save process", JOptionPane.ERROR_MESSAGE);
            } catch(CannotPauseException x) {
                JOptionPane.showMessageDialog(this, x.getMessage(), "Could not pause process", JOptionPane.ERROR_MESSAGE);
            }
        } else if(state == STATE_PAUSED) {
            // TODO: Add synchronization in case the process has not yet paused
            process.resume();
            setState(STATE_RUNNING);
        }
    }//GEN-LAST:event_startPauseButtonActionPerformed
    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JButton resumeButton;
    private javax.swing.JButton startPauseButton;
    // End of variables declaration//GEN-END:variables
    
    /**
     * Holds value of property processNotStartedText.
     */
    private String processNotStartedText;
    
    /**
     * Getter for property processNotStartedText.
     * @return Value of property processNotStartedText.
     */
    public String getProcessNotStartedText() {
        return this.processNotStartedText;
    }
    
    /**
     * Setter for property processNotStartedText.
     * @param processNotStartedText New value of property processNotStartedText.
     */
    public void setProcessNotStartedText(String processNotStartedText) {
        this.processNotStartedText = processNotStartedText;
        progressBar.setString(getProgressText());
    }
    
    /**
     * Holds value of property processCompletedText.
     */
    private String processCompletedText;
    
    /**
     * Getter for property processCompletedText.
     * @return Value of property processCompletedText.
     */
    public String getProcessCompletedText() {
        return this.processCompletedText;
    }
    
    /**
     * Setter for property processCompletedText.
     * @param processCompletedText New value of property processCompletedText.
     */
    public void setProcessCompletedText(String processCompletedText) {
        this.processCompletedText = processCompletedText;
        progressBar.setString(getProgressText());
    }
    
    private String progressText;
    public String getProgressText() {
        if(state == STATE_NOTSTARTED) {
            return getProcessNotStartedText();
        } else if(state == STATE_RUNNING) {
            return progressText;
        } else {
            return getProcessCompletedText();
        }
    }
    
    /**
     * Holds value of property process.
     */
    private AlloeProcess process;
    
    /**
     * Getter for property process.
     * @return Value of property process.
     */
    public AlloeProcess getProcess() {
        return this.process;
    }
    
    /**
     * Setter for property process.
     * @param process New value of property process.
     */
    public void setProcess(AlloeProcess process) {
        this.process = process;
        startPauseButton.setEnabled(process != null && state != STATE_COMPLETED);
    }
    
    public void finished() {
        progressBar.setValue(100);
        progressBar.setIndeterminate(false);
        progressBar.setString(getProcessCompletedText());
        setState(STATE_COMPLETED);
    }
    public void progressChange(double newProgress) {
        if(state == STATE_COMPLETED)
            return;
        if(newProgress >= 0.0 && newProgress <= 1.0) {
            progressBar.setIndeterminate(false);
            progressBar.setString(progressText = process.getStateMessage() + (int)(newProgress * 100) + "%");
            progressBar.setValue((int)(newProgress * 100));
        } else {
            progressBar.setIndeterminate(true);
            progressBar.setString(progressText = process.getStateMessage());
        }
    }
    
    private LinkedList<AlloeProgressListener> listeners;
    
    /** The point of this class is to register listeners to be readded on a resume action */
    public void addExtraListener(AlloeProgressListener apl) {
        listeners.add(apl);
    }
    
    private void setState(int state) {
        this.state = state;
        if(state == STATE_NOTSTARTED) {
            startPauseButton.setText("Start");
            startPauseButton.setEnabled(process != null);
            resumeButton.setEnabled(true);
        } else if(state == STATE_RUNNING) {
            startPauseButton.setText("Pause");
            startPauseButton.setEnabled(true);
            resumeButton.setEnabled(false);
        } else if(state == STATE_COMPLETED) {
            startPauseButton.setText("Clear");
            startPauseButton.setEnabled(true);
            resumeButton.setEnabled(true);
        } else if(state == STATE_PAUSED) {
            startPauseButton.setText("Unpause");
            startPauseButton.setEnabled(true);
            resumeButton.setEnabled(true);
        }
    }
    
    private LinkedList<ProcessMonitorListener> pmListeners;
    
    public JFileChooser getFileChooser() {
        return fileChooser;
    }
    
    public void addProcessMonitorListener(ProcessMonitorListener pml) {
        if(pmListeners == null)
            pmListeners = new LinkedList<ProcessMonitorListener>();
        if(!pmListeners.contains(pml))
            pmListeners.add(pml);
    }
    
    private boolean onStart() {
        if(pmListeners == null)
            return true;
        boolean rval = true;
        Iterator<ProcessMonitorListener> pmIter = pmListeners.iterator();
        while(pmIter.hasNext()) {
            rval = pmIter.next().onStart(this) && rval;
        }
        return rval;
    }
    
    private boolean onResume() {
        if(pmListeners == null)
            return true;
        boolean rval = true;
        Iterator<ProcessMonitorListener> pmIter = pmListeners.iterator();
        while(pmIter.hasNext()) {
            rval = pmIter.next().onResume(this) && rval;
        }
        return rval;
    }
}
